﻿/**

 * Copyright (c) 2015-2016, FastDev 刘强 (fastdev@163.com) & Quincy.

 *

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at

 *

 *      http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */

using OF.DistributeService.Core.Common;
using OF.Notify.Client;
using OF.Notify.Entity;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace OF.Notify.DataHost.Cluster
{
    public class TopicClusterContext
    {
        internal int[] collectionCountArray = new int[] { 256, 256 }; //2(32)  2(8)  2(24/3 = 8)
        internal int[] clusterCountArray = new int[] { 256, 256 }; //2(32)  2(8)  2(24/3 = 8)
        internal int[] messageCountArray = new int[] { 1024, 1024 };

        internal int[] bigValueCountArray = new int[] { 64, 64 };//2(32)  2(16-1)  2(17 + 1 / 3 = 6)
		
        internal byte topicEnum;
        internal string basePath = null;
        internal ConsumerInstanceProvider<ConsumerTopicClusterContext> consumerContextDict = null;
        internal Dictionary<byte, ConsumerBase> consumerDict;
        public TopicClusterContext(byte topicEnum, List<ConsumerBase> consumerList)
        {
            this.topicEnum = (byte)topicEnum;
            this.basePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "App_Data", this.topicEnum.ToString());
            Dictionary<byte, ConsumerTopicClusterContext> consumerContextParam = new Dictionary<byte, ConsumerTopicClusterContext>();
            consumerDict = new Dictionary<byte, ConsumerBase>();
            foreach (var consumer in consumerList)
            { 
                byte consumerEnum = consumer.GetEnumType();
                var consumerContext = new ConsumerTopicClusterContext(this, consumerEnum);
                try
                {
                    consumerContextParam.Add(consumerEnum, consumerContext);
                    consumerDict.Add(consumerEnum, consumer);
                }
                catch (Exception ex)
                {
                    Util.LogInfo("TopicClusterContext consumerEnum add failed, may be duplicate!Enum is:" + consumerEnum);
                    throw;
                }
            }
            consumerContextDict = new ConsumerInstanceProvider<ConsumerTopicClusterContext>(consumerContextParam);
        }

        public ConsumerTopicClusterContext GetConsumerContext(byte consumerEnum)
        {
            return consumerContextDict.GetInstance(consumerEnum);
        }

        public List<byte> GetAllConsumerEnums()
        {
            return consumerDict.Keys.ToList();
        }

        public ConsumerBase GetConsumer(byte consumerEnum)
        {
            if (!consumerDict.ContainsKey(consumerEnum))
            {
                throw new NotSupportedException("current KeyType is not supported " + consumerEnum);
            }
            return consumerDict[consumerEnum];
        }

        public string GetStorageBasePath()
        {
            return this.basePath;
        }

        public string GetCurrentNodeClusterStoragePath(int nodeId, int id)
        {
            return GetStoragePath(clusterCountArray, "Clusters", nodeId, id);
        }

        public string GetCollectionStoragePath(int nodeId, int id)
        {
            return GetStoragePath(collectionCountArray, "Collections", nodeId, id);
        }

        public string GetStorageCategoryPath(string category, int nodeId)
        {
            List<string> pathList = new List<string>(3);
            pathList.Add(GetStorageBasePath());
            pathList.Add(category);
            pathList.Add(nodeId.ToString());
            return Path.Combine(pathList.ToArray());
        }

        internal string GetStoragePath(int[] countArray, string category, int category2, int id)
        {
            int[] calArray = IdTreeNode.GetCalArray(countArray);
            int[] idPath = GetIdPath(calArray, id);

            List<string> pathList = new List<string>(idPath.Length + 3);
            pathList.Add(GetStorageBasePath());
            pathList.Add(category);
            pathList.Add(category2.ToString());

            pathList.AddRange(idPath.Select(item => item.ToString()));
            return Path.Combine(pathList.ToArray());
        }

        public string GetMasterStoragePath()
        {
            return Path.Combine(GetStorageBasePath(), "Master");
        }

        public string GetClusterMapNodeStoragePath(int nodeId, int clusterId)
        {
            return GetStoragePath(clusterCountArray, "ClusterMapNodes", nodeId, clusterId);
        }

        public string GetClusterMapNodeStorageBasePath(int nodeId)
        {
            return GetStorageCategoryPath("ClusterMapNodes", nodeId);
        }

        public string GetOfflineNodeChangeStorageBasePath(int nodeId, int offlineNodeId)
        {
            return GetStorageCategoryPath(Path.Combine("OfflineNodeChange", nodeId.ToString()), offlineNodeId);
        }

        public string GetOfflineNodeChangeStoragePath(int nodeId, int offlineNodeId, int clusterId)
        {
            return GetStoragePath(clusterCountArray, Path.Combine("OfflineNodeChange", nodeId.ToString()), offlineNodeId, clusterId);
        }

        public string GetMessagePath(int nodeId, int collectionId)
        {
            return GetStoragePath(messageCountArray, "Messages", nodeId, collectionId);
        }
       
        public string GetTempMessagePath(int nodeId, int clusterId, int collectionId)
        {
            return GetStoragePath(messageCountArray, Path.Combine("TempMessages", nodeId.ToString()), clusterId, collectionId);
        }

        public string GetTempMessageBasePath(int nodeId, int clusterId)
        {
            List<string> pathList = new List<string>(3);
            pathList.Add(GetStorageBasePath());
            pathList.Add(Path.Combine("TempMessages", nodeId.ToString()));
            pathList.Add(clusterId.ToString());
            return Path.Combine(pathList.ToArray());
        }

        public List<int> GetAllTempMessageClusters(int nodeId)
        {
            List<string> pathList = new List<string>(3);
            pathList.Add(GetStorageBasePath());
            pathList.Add(Path.Combine("TempMessages", nodeId.ToString()));
            string parentPath = Path.Combine(pathList.ToArray());
            if (Directory.Exists(parentPath))
            {
                List<int> result = new List<int>();
                foreach (var clusterTempPath in Directory.EnumerateDirectories(parentPath))
                {
                    string sClusterId = clusterTempPath.Substring(clusterTempPath.LastIndexOf('\\') + 1);
                    result.Add(int.Parse(sClusterId));
                }
                return result;
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// Id is index from 0
        /// </summary>
        /// <param name="calArray"></param>
        /// <param name="id"></param>
        /// <returns></returns>
        public static int[] GetIdPath(int[] calArray, int id)
        {
            int[] pathArray = new int[calArray.Length + 1];
            for (int i1 = 0; i1 < calArray.Length; i1++)
            {
                int currentPathIndex = id / calArray[i1];
                pathArray[i1] = currentPathIndex;
                id = id - currentPathIndex * calArray[i1];
            }
            pathArray[calArray.Length] = id;
            return pathArray;
        }


        public string GetIdBigValuePath(string idPath, int combineRate, int value)
        {
            int[] calArray = IdTreeNode.GetCalArray(bigValueCountArray);
            int[] valuePath = GetIdPath(calArray, value / combineRate);
            string[] strArr = new string[valuePath.Length + 1];
            strArr[0] = idPath;
            for (int i1 = 0; i1 < valuePath.Length; i1++)
            {
                strArr[i1 + 1] = valuePath[i1].ToString();
            }
            return Path.Combine(strArr);
        }
    }
}
